<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\VarDumper\Tests\Caster;

use PHPUnit\Framework\Attributes\RequiresPhpExtension;
use PHPUnit\Framework\TestCase;
use Symfony\Component\VarDumper\Test\VarDumperTestTrait;

#[RequiresPhpExtension('intl')]
class IntlCasterTest extends TestCase
{
    use VarDumperTestTrait;

    public function testMessageFormatter()
    {
        $var = new \MessageFormatter('en', 'Hello {name}');

        $expected = <<<EOTXT
            MessageFormatter {
              locale: "en"
              pattern: "Hello {name}"
            }
            EOTXT;
        $this->assertDumpEquals($expected, $var);
    }

    public function testCastNumberFormatter()
    {
        $var = new \NumberFormatter('en', \NumberFormatter::DECIMAL);

        $expectedLocale = $var->getLocale();
        $expectedPattern = $var->getPattern();

        $expectedAttribute1 = $var->getAttribute(\NumberFormatter::PARSE_INT_ONLY);
        $expectedAttribute2 = $var->getAttribute(\NumberFormatter::GROUPING_USED);
        $expectedAttribute3 = $var->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN);
        $expectedAttribute4 = $var->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS);
        $expectedAttribute5 = $var->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS);
        $expectedAttribute6 = $var->getAttribute(\NumberFormatter::INTEGER_DIGITS);
        $expectedAttribute7 = $var->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS);
        $expectedAttribute8 = $var->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS);
        $expectedAttribute9 = $var->getAttribute(\NumberFormatter::FRACTION_DIGITS);
        $expectedAttribute10 = $var->getAttribute(\NumberFormatter::MULTIPLIER);
        $expectedAttribute11 = $var->getAttribute(\NumberFormatter::GROUPING_SIZE);
        $expectedAttribute12 = $var->getAttribute(\NumberFormatter::ROUNDING_MODE);
        $expectedAttribute13 = number_format($var->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), 1);
        $expectedAttribute14 = $this->getDump($var->getAttribute(\NumberFormatter::FORMAT_WIDTH));
        $expectedAttribute15 = $var->getAttribute(\NumberFormatter::PADDING_POSITION);
        $expectedAttribute16 = $var->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE);
        $expectedAttribute17 = $var->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED);
        $expectedAttribute18 = $this->getDump($var->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS));
        $expectedAttribute19 = $this->getDump($var->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS));
        $expectedAttribute20 = $var->getAttribute(\NumberFormatter::LENIENT_PARSE);

        $expectedTextAttribute1 = $var->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX);
        $expectedTextAttribute2 = $var->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX);
        $expectedTextAttribute3 = $var->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX);
        $expectedTextAttribute4 = $var->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX);
        $expectedTextAttribute5 = $var->getTextAttribute(\NumberFormatter::PADDING_CHARACTER);
        $expectedTextAttribute6 = $var->getTextAttribute(\NumberFormatter::CURRENCY_CODE);
        $expectedTextAttribute7 = $var->getTextAttribute(\NumberFormatter::DEFAULT_RULESET) ? 'true' : 'false';
        $expectedTextAttribute8 = $var->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS) ? 'true' : 'false';

        $expectedSymbol1 = $var->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
        $expectedSymbol2 = $var->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
        $expectedSymbol3 = $var->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL);
        $expectedSymbol4 = $var->getSymbol(\NumberFormatter::PERCENT_SYMBOL);
        $expectedSymbol5 = $var->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL);
        $expectedSymbol6 = $var->getSymbol(\NumberFormatter::DIGIT_SYMBOL);
        $expectedSymbol7 = $var->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL);
        $expectedSymbol8 = $var->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL);
        $expectedSymbol9 = $var->getSymbol(\NumberFormatter::CURRENCY_SYMBOL);
        $expectedSymbol10 = $var->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL);
        $expectedSymbol11 = $var->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
        $expectedSymbol12 = $var->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL);
        $expectedSymbol13 = $var->getSymbol(\NumberFormatter::PERMILL_SYMBOL);
        $expectedSymbol14 = $var->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL);
        $expectedSymbol15 = $var->getSymbol(\NumberFormatter::INFINITY_SYMBOL);
        $expectedSymbol16 = $var->getSymbol(\NumberFormatter::NAN_SYMBOL);
        $expectedSymbol17 = $var->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL);
        $expectedSymbol18 = $var->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);

        $expected = <<<EOTXT
            NumberFormatter {
              locale: "$expectedLocale"
              pattern: "$expectedPattern"
              attributes: {
                PARSE_INT_ONLY: $expectedAttribute1
                GROUPING_USED: $expectedAttribute2
                DECIMAL_ALWAYS_SHOWN: $expectedAttribute3
                MAX_INTEGER_DIGITS: $expectedAttribute4
                MIN_INTEGER_DIGITS: $expectedAttribute5
                INTEGER_DIGITS: $expectedAttribute6
                MAX_FRACTION_DIGITS: $expectedAttribute7
                MIN_FRACTION_DIGITS: $expectedAttribute8
                FRACTION_DIGITS: $expectedAttribute9
                MULTIPLIER: $expectedAttribute10
                GROUPING_SIZE: $expectedAttribute11
                ROUNDING_MODE: $expectedAttribute12
                ROUNDING_INCREMENT: $expectedAttribute13
                FORMAT_WIDTH: $expectedAttribute14
                PADDING_POSITION: $expectedAttribute15
                SECONDARY_GROUPING_SIZE: $expectedAttribute16
                SIGNIFICANT_DIGITS_USED: $expectedAttribute17
                MIN_SIGNIFICANT_DIGITS: $expectedAttribute18
                MAX_SIGNIFICANT_DIGITS: $expectedAttribute19
                LENIENT_PARSE: $expectedAttribute20
              }
              text_attributes: {
                POSITIVE_PREFIX: "$expectedTextAttribute1"
                POSITIVE_SUFFIX: "$expectedTextAttribute2"
                NEGATIVE_PREFIX: "$expectedTextAttribute3"
                NEGATIVE_SUFFIX: "$expectedTextAttribute4"
                PADDING_CHARACTER: "$expectedTextAttribute5"
                CURRENCY_CODE: "$expectedTextAttribute6"
                DEFAULT_RULESET: $expectedTextAttribute7
                PUBLIC_RULESETS: $expectedTextAttribute8
              }
              symbols: {
                DECIMAL_SEPARATOR_SYMBOL: "$expectedSymbol1"
                GROUPING_SEPARATOR_SYMBOL: "$expectedSymbol2"
                PATTERN_SEPARATOR_SYMBOL: "$expectedSymbol3"
                PERCENT_SYMBOL: "$expectedSymbol4"
                ZERO_DIGIT_SYMBOL: "$expectedSymbol5"
                DIGIT_SYMBOL: "$expectedSymbol6"
                MINUS_SIGN_SYMBOL: "$expectedSymbol7"
                PLUS_SIGN_SYMBOL: "$expectedSymbol8"
                CURRENCY_SYMBOL: "$expectedSymbol9"
                INTL_CURRENCY_SYMBOL: "$expectedSymbol10"
                MONETARY_SEPARATOR_SYMBOL: "$expectedSymbol11"
                EXPONENTIAL_SYMBOL: "$expectedSymbol12"
                PERMILL_SYMBOL: "$expectedSymbol13"
                PAD_ESCAPE_SYMBOL: "$expectedSymbol14"
                INFINITY_SYMBOL: "$expectedSymbol15"
                NAN_SYMBOL: "$expectedSymbol16"
                SIGNIFICANT_DIGIT_SYMBOL: "$expectedSymbol17"
                MONETARY_GROUPING_SEPARATOR_SYMBOL: "$expectedSymbol18"
              }
            }
            EOTXT;
        $this->assertDumpEquals($expected, $var);
    }

    public function testCastIntlTimeZoneWithDST()
    {
        $var = \IntlTimeZone::createTimeZone('America/Los_Angeles');

        $expectedDisplayName = $var->getDisplayName();
        $expectedDSTSavings = $var->getDSTSavings();
        $expectedID = $var->getID();
        $expectedRawOffset = $var->getRawOffset();

        $expected = <<<EOTXT
            IntlTimeZone {
              display_name: "$expectedDisplayName"
              id: "$expectedID"
              raw_offset: $expectedRawOffset
              dst_savings: $expectedDSTSavings
            }
            EOTXT;
        $this->assertDumpEquals($expected, $var);
    }

    public function testCastIntlTimeZoneWithoutDST()
    {
        $var = \IntlTimeZone::createTimeZone('Asia/Bangkok');

        $expectedDisplayName = $var->getDisplayName();
        $expectedID = $var->getID();
        $expectedRawOffset = $var->getRawOffset();

        $expected = <<<EOTXT
            IntlTimeZone {
              display_name: "$expectedDisplayName"
              id: "$expectedID"
              raw_offset: $expectedRawOffset
            }
            EOTXT;
        $this->assertDumpEquals($expected, $var);
    }

    public function testCastIntlCalendar()
    {
        $var = \IntlCalendar::createInstance('America/Los_Angeles', 'en');

        $expectedType = $var->getType();
        $expectedFirstDayOfWeek = $var->getFirstDayOfWeek();
        $expectedMinimalDaysInFirstWeek = $var->getMinimalDaysInFirstWeek();
        $expectedRepeatedWallTimeOption = $var->getRepeatedWallTimeOption();
        $expectedSkippedWallTimeOption = $var->getSkippedWallTimeOption();
        $expectedTime = $var->getTime().'.0';
        $expectedInDaylightTime = $var->inDaylightTime() ? 'true' : 'false';
        $expectedIsLenient = $var->isLenient() ? 'true' : 'false';

        $expectedTimeZone = $var->getTimeZone();
        $expectedTimeZoneDisplayName = $expectedTimeZone->getDisplayName();
        $expectedTimeZoneID = $expectedTimeZone->getID();
        $expectedTimeZoneRawOffset = $expectedTimeZone->getRawOffset();
        $expectedTimeZoneDSTSavings = $expectedTimeZone->getDSTSavings();

        $expected = <<<EOTXT
            IntlGregorianCalendar {
              type: "$expectedType"
              first_day_of_week: $expectedFirstDayOfWeek
              minimal_days_in_first_week: $expectedMinimalDaysInFirstWeek
              repeated_wall_time_option: $expectedRepeatedWallTimeOption
              skipped_wall_time_option: $expectedSkippedWallTimeOption
              time: $expectedTime
              in_daylight_time: $expectedInDaylightTime
              is_lenient: $expectedIsLenient
              time_zone: IntlTimeZone {
                display_name: "$expectedTimeZoneDisplayName"
                id: "$expectedTimeZoneID"
                raw_offset: $expectedTimeZoneRawOffset
                dst_savings: $expectedTimeZoneDSTSavings
              }
            }
            EOTXT;
        $this->assertDumpEquals($expected, $var);
    }

    public function testCastDateFormatter()
    {
        $var = new \IntlDateFormatter('en', \IntlDateFormatter::TRADITIONAL, \IntlDateFormatter::TRADITIONAL);

        $expectedLocale = $var->getLocale();
        $expectedPattern = $this->normalizeNarrowNoBreakSpaceCharacter($var->getPattern());
        $expectedCalendar = $var->getCalendar();
        $expectedTimeZoneId = $var->getTimeZoneId();
        $expectedTimeType = $var->getTimeType();
        $expectedDateType = $var->getDateType();

        $expectedTimeZone = $var->getTimeZone();
        $expectedTimeZoneDisplayName = $expectedTimeZone->getDisplayName();
        $expectedTimeZoneID = $expectedTimeZone->getID();
        $expectedTimeZoneRawOffset = $expectedTimeZone->getRawOffset();
        $expectedTimeZoneDSTSavings = $expectedTimeZone->useDaylightTime() ? "\n    dst_savings: ".$expectedTimeZone->getDSTSavings() : '';

        $expectedCalendarObject = $var->getCalendarObject();
        $expectedCalendarObjectType = $expectedCalendarObject->getType();
        $expectedCalendarObjectFirstDayOfWeek = $expectedCalendarObject->getFirstDayOfWeek();
        $expectedCalendarObjectMinimalDaysInFirstWeek = $expectedCalendarObject->getMinimalDaysInFirstWeek();
        $expectedCalendarObjectRepeatedWallTimeOption = $expectedCalendarObject->getRepeatedWallTimeOption();
        $expectedCalendarObjectSkippedWallTimeOption = $expectedCalendarObject->getSkippedWallTimeOption();
        $expectedCalendarObjectTime = $expectedCalendarObject->getTime().'.0';
        $expectedCalendarObjectInDaylightTime = $expectedCalendarObject->inDaylightTime() ? 'true' : 'false';
        $expectedCalendarObjectIsLenient = $expectedCalendarObject->isLenient() ? 'true' : 'false';

        $expectedCalendarObjectTimeZone = $expectedCalendarObject->getTimeZone();
        $expectedCalendarObjectTimeZoneDisplayName = $expectedCalendarObjectTimeZone->getDisplayName();
        $expectedCalendarObjectTimeZoneID = $expectedCalendarObjectTimeZone->getID();
        $expectedCalendarObjectTimeZoneRawOffset = $expectedCalendarObjectTimeZone->getRawOffset();
        $expectedCalendarObjectTimeZoneDSTSavings = $expectedTimeZone->useDaylightTime() ? "\n      dst_savings: ".$expectedCalendarObjectTimeZone->getDSTSavings() : '';

        $expected = <<<EOTXT
            IntlDateFormatter {
              locale: "$expectedLocale"
              pattern: "$expectedPattern"
              calendar: $expectedCalendar
              time_zone_id: "$expectedTimeZoneId"
              time_type: $expectedTimeType
              date_type: $expectedDateType
              calendar_object: IntlGregorianCalendar {
                type: "$expectedCalendarObjectType"
                first_day_of_week: $expectedCalendarObjectFirstDayOfWeek
                minimal_days_in_first_week: $expectedCalendarObjectMinimalDaysInFirstWeek
                repeated_wall_time_option: $expectedCalendarObjectRepeatedWallTimeOption
                skipped_wall_time_option: $expectedCalendarObjectSkippedWallTimeOption
                time: $expectedCalendarObjectTime
                in_daylight_time: $expectedCalendarObjectInDaylightTime
                is_lenient: $expectedCalendarObjectIsLenient
                time_zone: IntlTimeZone {
                  display_name: "$expectedCalendarObjectTimeZoneDisplayName"
                  id: "$expectedCalendarObjectTimeZoneID"
                  raw_offset: $expectedCalendarObjectTimeZoneRawOffset$expectedCalendarObjectTimeZoneDSTSavings
                }
              }
              time_zone: IntlTimeZone {
                display_name: "$expectedTimeZoneDisplayName"
                id: "$expectedTimeZoneID"
                raw_offset: $expectedTimeZoneRawOffset$expectedTimeZoneDSTSavings
              }
            }
            EOTXT;
        $this->assertDumpEquals($expected, $var);
    }

    private function normalizeNarrowNoBreakSpaceCharacter(string $input): string
    {
        return str_replace("\u{202F}", '\\u{202F}', $input);
    }
}
